package cn.zn.framework.aspect;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import cn.zn.base.annotation.DataScope;
import cn.zn.base.bean.vo.BasePageVo;
import cn.zn.base.common.utils.AdminUtil;
import cn.zn.framework.utils.SecurityUtil;
import cn.zn.role.entity.SysRole;
import cn.zn.user.entity.SysUser;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.Signature;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 数据过滤处理
 *
 * @author zn-admin
 */
@Aspect
@Component
public class DataScopeAspect {

    /**
     * 全部数据权限
     */
    public static final String DATA_SCOPE_ALL = "1";

    /**
     * 所属组织及其下级数据权限
     */
    public static final String DATA_SCOPE_ORG = "2";

    /**
     * 所属部门数据权限
     */
    public static final String DATA_SCOPE_DEPT = "3";

    /**
     * 仅本人数据权限
     */
    public static final String DATA_SCOPE_SELF = "4";

    // 配置织入点
    @Pointcut("@annotation(cn.zn.base.annotation.DataScope)")
    public void dataScopePointCut() {
    }

    @Before("dataScopePointCut()")
    public void doBefore(JoinPoint point) throws Throwable {
        handleDataScope(point);
    }

    protected void handleDataScope(final JoinPoint joinPoint) {
        // 获得注解
        DataScope controllerDataScope = getAnnotationLog(joinPoint);
        if (controllerDataScope == null) {
            return;
        }
        // 获取当前的用户
        SysUser currentUser = SecurityUtil.getTheUser();
        // 如果是超级管理员，则不过滤数据
        if (ObjectUtil.isNotNull(currentUser) && !AdminUtil.isAdmin(currentUser.getLoginName())) {
            dataScopeFilter(joinPoint, currentUser, controllerDataScope.deptFieldName(), controllerDataScope.userFieldName(), controllerDataScope.frFieldName(), controllerDataScope.selfEffective());
        }
    }

    /**
     * 数据范围过滤
     *
     * @param joinPoint     切点
     * @param user          用户
     * @param userFieldName 别名
     */
    public static void dataScopeFilter(JoinPoint joinPoint, SysUser user, String deptFieldName, String userFieldName, String frFieldName, boolean selfEffective) {
        // 合并数据权限
        Set<String> dataPermissions = new HashSet<>();
        String dataPermission = DATA_SCOPE_SELF;
        Set<String> orgIds = new HashSet<>();
        Set<String> frOrgIds = new HashSet<>();
        for (SysRole role : user.getRoles()) {
            String dataScope = role.getDataScope();
            dataPermissions.add(dataScope);
        }

        if (dataPermissions.contains(DATA_SCOPE_DEPT)) {
            dataPermission = DATA_SCOPE_DEPT;
        }
        if (dataPermissions.contains(DATA_SCOPE_ORG)) {
            dataPermission = DATA_SCOPE_ORG;
        }
        if (dataPermissions.contains(DATA_SCOPE_ALL)) {
            dataPermission = DATA_SCOPE_ALL;
        }

        // 兼职权限或本组织权限
        if (DATA_SCOPE_ORG.equals(dataPermission) || DATA_SCOPE_DEPT.equals(dataPermission)) {
            if (CollUtil.isNotEmpty(user.getJzOrgIds())) {
                orgIds.addAll(user.getJzOrgIds());
            }

            if (CollUtil.isNotEmpty(user.getFrOrgIds())) {
                frOrgIds.addAll(user.getFrOrgIds());
            }
        }

        String dataScopeSql = getDataScopeSql(dataPermission, orgIds, user, deptFieldName, userFieldName, selfEffective);
        String frDataScopeSql = getFrDataScopeSql(dataPermission, frOrgIds, frFieldName);
        if (StrUtil.isNotBlank(dataScopeSql)) {
            Object params = joinPoint.getArgs()[0];
            if (ObjectUtil.isNotNull(params) && params instanceof BasePageVo) {
                BasePageVo basePageVo = (BasePageVo) params;
                basePageVo.setDataScope(dataScopeSql);
                basePageVo.setFrDataScope(frDataScopeSql);
            }
        }
    }

    private static String getDataScopeSql(String dataPermission, Set<String> orgIds, SysUser user, String deptFieldName, String userFieldName, boolean selfEffective) {
        // 如果有所有权限，直接返回
        if (StrUtil.equals(DATA_SCOPE_ALL, dataPermission)) {
            return null;
        }

        // 个人权限
        if (StrUtil.equals(DATA_SCOPE_SELF, dataPermission)) {
            // 如果本人数据权限有效，则按本人取数
            if (selfEffective) {
                StringBuilder sqlString = new StringBuilder();
                if (StrUtil.isNotBlank(userFieldName)) {
                    sqlString.append(StrUtil.format(" AND {} = '{}' ", userFieldName, user.getId()));
                } else {
                    // 数据权限为仅本人且没有userAlias别名不查询任何数据
                    sqlString.append(" AND 1 = 0 ");
                }
                return sqlString.toString();
            }
        }

        StringBuilder sqlString = new StringBuilder();
        // 默认用户所属组织
        StringBuilder deptSql = new StringBuilder();
        if (StrUtil.equals(DATA_SCOPE_ORG, dataPermission)) {
            orgIds.add(user.getOrgId());
        } else {
            orgIds.add(user.getDeptId());
        }

        deptSql.append(StrUtil.format(" select id from sys_dept start with id in ({}) connect by prior id = parent_id ", "'" + CollUtil.join(orgIds, "', '") + "'"));

        sqlString.append(StrUtil.format(" AND {} in ({}) ", deptFieldName, deptSql.toString()));
        return sqlString.toString();
    }

    private static String getFrDataScopeSql(String dataPermission, Set<String> frOrgIds, String frFieldName) {
        if (StrUtil.equals(DATA_SCOPE_ORG, dataPermission) || StrUtil.equals(DATA_SCOPE_DEPT, dataPermission)) {
            if (StrUtil.isNotBlank(frFieldName) && CollUtil.isNotEmpty(frOrgIds)) {
                return StrUtil.format(" AND {} in ({}) ", frFieldName, "'" + CollUtil.join(frOrgIds, "', '") + "'");
            }
        }
        return null;
    }

    /**
     * 是否存在注解，如果存在就获取
     */
    private DataScope getAnnotationLog(JoinPoint joinPoint) {
        Signature signature = joinPoint.getSignature();
        MethodSignature methodSignature = (MethodSignature) signature;
        Method method = methodSignature.getMethod();

        if (method != null) {
            return method.getAnnotation(DataScope.class);
        }
        return null;
    }
}
